package scatter.area.rest.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import scatter.area.pojo.dto.AreaIdResolveDto;
import scatter.area.pojo.form.AreaAddForm;
import scatter.area.pojo.form.AreaPageQueryForm;
import scatter.area.pojo.form.AreaUpdateForm;
import scatter.area.pojo.param.AreaIdResolveParam;
import scatter.area.pojo.po.Area;
import scatter.area.rest.mapper.AreaMapper;
import scatter.area.rest.service.IAreaService;
import scatter.common.rest.service.IBaseAddUpdateQueryFormServiceImpl;
import scatter.common.rest.tools.BaiduMapTool;
import scatter.common.rest.tools.Pinyin;
import scatter.common.rest.validation.DictService;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 区域表 服务实现类
 * </p>
 *
 * @author yw
 * @since 2020-11-12
 */
@Service
@Transactional
@Slf4j
public class AreaServiceImpl extends IBaseAddUpdateQueryFormServiceImpl<AreaMapper, Area, AreaAddForm, AreaUpdateForm, AreaPageQueryForm> implements IAreaService {

    @Value("${scatter.baidu.map.ak:}")
    private String baiduMapAk;

    @Autowired
    private DictService dictService;

    @Override
    public void preAdd(AreaAddForm addForm, Area po) {
        super.preAdd(addForm,po);
        if (!isStrEmpty(po.getCode())) {
            // 编码已存在不能添加
            assertByColumn(po.getCode(),Area::getCode,false);
        }
        // 添加拼音
        try {
            Pinyin pinyin = getPinyin(po.getName());
            po.setSpellFirst(pinyin.getFirst());
            po.setSpellSimple(pinyin.getSimple());
            po.setSpell(pinyin.getFull());
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            log.error(e.getMessage() + "{}",e,po.getName());
        }
        // 获取经纬度
        if(!isStrEmpty(baiduMapAk)){
            String getParentNames = "";
            if (!isStrEmpty(po.getParentId())) {
                List<Area> allParents = getAllParents(po.getParentId());
                Area byId = getById(po.getParentId());
                allParents.add(byId);
                getParentNames = allParents.stream().map(Area::getName).collect(Collectors.joining(" "));
            }
            Map<String, Object> r = BaiduMapTool.getGeo(getParentNames + " " + po.getName(), baiduMapAk);

            if ("0".equals(r.get("status").toString())) {
                Map<String, Object> result = (Map<String, Object>) r.get("result");
                Map<String, Object> location = (Map<String, Object>) result.get("location");
                po.setLongitude(location.get("lng").toString());
                po.setLatitude(location.get("lat").toString());
            }
        }else {
            log.warn("未配置baidu.map.ak,未能获取经纬度坐标 {}",po.getName());
        }
    }

    @Override
    public void preUpdate(AreaUpdateForm updateForm,Area po) {
        super.preUpdate(updateForm,po);
        Area byId = getById(po.getId());
        if (!isStrEmpty(po.getCode())) {
            // 如果编码有改动
            if (!isEqual(po.getCode(), byId.getCode())) {
                // 编码已存在不能修改
                assertByColumn(po.getCode(),Area::getCode,false);
            }
        }
        // 如果名字有改动重新设置拼音
        if(!isEqual(po.getName(),byId.getName())){
            try {
                Pinyin pinyin = getPinyin(po.getName());
                po.setSpellFirst(pinyin.getFirst());
                po.setSpellSimple(pinyin.getSimple());
                po.setSpell(pinyin.getFull());
            } catch (BadHanyuPinyinOutputFormatCombination e) {
                log.error(e.getMessage() + "{}",e,po.getName());
            }
        }
        // 获取经纬度
        if(!isStrEmpty(baiduMapAk)){
            String getParentNames = "";
            if (!isStrEmpty(po.getParentId())) {
                List<Area> allParents = getAllParents(po.getParentId());
                Area byParentId = getById(po.getParentId());
                allParents.add(byParentId);
                getParentNames = allParents.stream().map(Area::getName).collect(Collectors.joining(" "));
            }
            Map<String, Object> r = BaiduMapTool.getGeo(getParentNames + " " + po.getName(), baiduMapAk);

            if ("0".equals(r.get("status").toString())) {
                Map<String, Object> result = (Map<String, Object>) r.get("result");
                Map<String, Object> location = (Map<String, Object>) result.get("location");
                po.setLongitude(location.get("lng").toString());
                po.setLatitude(location.get("lat").toString());
            }
        }else {
            log.warn("未配置baidu.map.ak,未能获取经纬度坐标 {}",po.getName());
        }
    }

    @Override
    public List<Area> getListByName(String name, Area.TypeDictItem typeDictItem, String parentId) {

        String typeDictId = dictService.getIdByGroupCodeAndValue(Area.TypeDictGroup.area_type.groupCode(), typeDictItem.itemValue());
        LambdaQueryWrapper<Area> queryWrapper = Wrappers.<Area>lambdaQuery()
                .and(wp->{
                    wp.like(Area::getName, name).or().like(Area::getNameSimple,name);
                })
                .eq(!isStrEmpty(parentId),Area::getParentId,parentId)
                .eq(Area::getTypeDictId, typeDictId);
        List<Area> list = list(queryWrapper);
        if (!isEmpty(list)) {
            if(  list.size() >1 ){
                log.warn("根据名字获取区域数据返回多个 name={},type={},parentId={}",name,typeDictItem.itemValue(),parentId);
            }
        }else {
            log.warn("根据名字获取区域,无数据 name={},type={},parentId={}",name,typeDictItem.itemValue(),parentId);
        }
        return list;
    }


    /**
     * 定义三个缓存
     */
    private static Map<String,String> countryCache = new HashMap<>();
    private static Map<String,String> provinceCache = new HashMap<>();
    private static Map<String,String> cityCache = new HashMap<>();
    private static Map<String,String> districtCache = new HashMap<>();



    @Override
    public AreaIdResolveDto resolveName(AreaIdResolveParam resolveParam) {

        //        国家
        String countryId = null;
        if (!isStrEmpty(resolveParam.getCountryName())) {
            countryId = countryCache.computeIfAbsent(resolveParam.getCountryName(),k->{

                Area country = getOneByName(resolveParam.getCountryName(), Area.TypeDictItem.country, null);
                if (country != null) {
                    return  country.getId();
                }
                return null;
            });

        }

        //        省
        String provinceId = null;
        String provinceParentId = countryId;
        if (!isStrEmpty(resolveParam.getProvinceName())) {
            provinceId = provinceCache.computeIfAbsent(resolveParam.getProvinceName(),k->{

                Area province = getOneByName(resolveParam.getProvinceName(), Area.TypeDictItem.province, provinceParentId);
                if (province != null) {
                    return  province.getId();
                }

                return null;
            });

        }

        //        市
        String cityId = null;
        String cityParentId = provinceId;
        if (!isStrEmpty(resolveParam.getCityName())) {
            cityId = cityCache.computeIfAbsent(resolveParam.getCityName(),k -> {
                Area city = getOneByName(resolveParam.getCityName(), Area.TypeDictItem.city, cityParentId);
                if (city != null) {
                    return city.getId();
                }

                return null;
            });

        }
        //        区
        String districtId = null;
        String districtParentId = cityId;
        if (!isStrEmpty(resolveParam.getDistrictName())) {
            districtId = districtCache.computeIfAbsent(resolveParam.getDistrictName(),k -> {
                Area county = getOneByName(resolveParam.getDistrictName(), Area.TypeDictItem.county, districtParentId);
                if (county != null) {
                    return county.getId();
                }

                return null;
            });

        }
        AreaIdResolveDto areaIdResolveDto = new AreaIdResolveDto();
        areaIdResolveDto.setCountryId(countryId);
        areaIdResolveDto.setProvinceId(provinceId);
        areaIdResolveDto.setCityId(cityId);
        areaIdResolveDto.setDistrictId(districtId);
        return areaIdResolveDto;
    }
}
